
from yacf.Frontend.Shortcuts import getCurrentLanguageAst
from yacf.Backends.Common.TemplateEngine.Functions import size_of_var
c_ast = getCurrentLanguageAst()

from yacf.Tools.Tree import getFileNode
from yacf.Backends.C99.Writers.C99Writer import CWriter

from yacf.Backends.Common.Visitors.GenericVisitors import IdentifierTypeFilter, TypedefFilter
from yacf.Frontend.SymbolTable import getSymbolTableFromNode, ComposedSymbol
import cStringIO


from yacf.Tools.Tree import NodeNotFound

from mako.template import Template as MakoTemplate
from mako.lookup import TemplateLookup 
import config


class TemplateParser(MakoTemplate):
   pass

class BackendTemplateParser(TemplateParser):
    def __init__(self, backend_name, *args, **kwargs):
#        print " Path :" + config.WORKDIR + "Backends/" + backend_name + '/Templates/',
        lookup = TemplateLookup(directories = [config.WORKDIR + "Backends/" + backend_name + '/Templates/',], filesystem_checks = True,)
        kwargs['lookup'] = lookup
        super(TemplateParser, self).__init__(*args, **kwargs)



class TemplateVarNode(object):
    """ The representation of a node in the Template 
    """

    #===========================================================================
    # def fast_write(self, elem):
    #    """ Print the node 
    #        :param elem: Node to be printed
    #    """
    #    writeIO = cStringIO.StringIO()
    #    cw = CWriter(stream = writeIO)
    #    cw.visit(elem)
    #    return writeIO.getvalue()
    #===========================================================================

    def decl_write(self, elem):
        """ Write the declaration of the type of an element
                in the original language.
        
            Minor modifications are needed.
        
            :param elem: Declaration node
        """
        tmp = " ".join(['%s'%stor for stor in elem.storage]) 
        if isinstance(elem.type, c_ast.ArrayDecl):
            tmp += " "
            tmp_node = elem.type
            while not (isinstance(tmp_node, c_ast.TypeDecl) or isinstance(tmp_node, c_ast.PtrDecl)) and tmp_node:
                tmp_node = tmp_node.type

            tmp += str(tmp_node) #self.fast_write(tmp_node)
        else:
            tmp += " ".join(['%s'%qual for qual in elem.quals])
            tmp += " "
            tmp += str(elem.type) # self.fast_write(elem.type)
        return tmp

    def _get_typedef_from_node(self, node, subtree):
        """ Build a list with all type declarations from a declaration list 
    
        :param node: Node to get its typedef
                            typedefs needs to be extracted
        :param subtree: Place to search the typedefs
    
        """
        typedef_decl = ""
        try:
            # Search for  the type of the element
            identifier_type = IdentifierTypeFilter().apply(node.type)
            # Look for its type definition in the subtree
            typedef_node = TypedefFilter(name = identifier_type.names[0])
            tmp = typedef_node.apply(getFileNode(subtree))
            # TODO: Avoid construction/destruction
            typedefIO = cStringIO.StringIO()
            cw = CWriter(stream = typedefIO)
            cw.visit(tmp)
            typedef_decl  = str(typedefIO.getvalue())
        except NodeNotFound as nnf:
#            print str(nnf)
            # It is not a complex type
            # print str(nnf)
            # print " Not a user defined-type " + str(elem)
            pass
        
        # print "What I found: " + typedef_decl
        return typedef_decl

    def _get_structdecl_from_node(self, node, subtree):
        """ Build a list with all struct declarations from a declaration list 
    
        :param node: Node to get its struct declaration
        :param subtree: Place to search the struct declarations
    
        """
        # Quick return if possible
        if type(node.type.type) != c_ast.Struct:
            return  " "
        
        
        if isinstance(self._symbol.btype, ComposedSymbol):
            structIO = cStringIO.StringIO()
            cw = CWriter(stream = structIO)
            cw.visit(self._symbol.btype.node)
            structDecl_str  = str(structIO.getvalue())
            return structDecl_str + ";"
        else:
            return " "


    def __init__(self, var, ast, 
                 name_func = lambda elem : elem.name, 
                 type_func = lambda elem: elem.type, st = None, symbol = None):
        """ Build the TemplateVarNode making a clausure
                with the modifier functions.
        """
        self._var = var
        self._ast = ast
        self._type = var.type
        self._type_func = type_func
        self._name_func = name_func
        
        # Get the symbol table from any node (they share the same FileAST)
        if not st:
            self._st = getSymbolTableFromNode(getFileNode(var))
        else:
            self._st = st
        if not symbol:
            self._symbol = self._st.lookUpDecl(var)
        else:
            self._symbol = symbol
        
        self._typedef = self._get_typedef_from_node(var, ast)
        self._structDecl = self._get_structdecl_from_node(var, ast)

    def __str__(self):
        return self._name_func(self._var)

    @property
    def name(self):
        """ Return the name of the node
                after the name function
        """
        return self._name_func(self._var)

    @property
    def type(self):
        """ Return the type of the node
                after the type function
        """
        return self._type_func(self._type)

    @property
    def declaration(self):
        """ Print the declaration of the element as a sentence 
        """
        return self.decl_write(self._var).replace(';','').replace('\n','')

    @property
    def numelems(self):
        """ Number of elements in case of an array       
        """
        numelems = "1"
        if isinstance(self.ptr.type, c_ast.ArrayDecl): 
            numelems = str(self.ptr.type.dim)
        return numelems

    @property
    def size(self):
        """ Size of a data type
        """
        return str(self._symbol.sizeExpression)


    @property
    def ptr(self):
        return self._var

    @property
    def typedef(self):
        return self._typedef

    @property
    def structDecl(self):
        return self._structDecl


def get_template_array(var_list, ast, func = lambda elem : True, 
                        name_func = lambda elem : elem.name, 
                        type_func = lambda elem : elem.type, st = None):
    """ Convert an array list to a template array 
    
        :param var_list: List with Decl elements
        :param ast: Subtree
        :param func: Type of element from var_list which is added to the template array
        :param name_func: Function to get the name of each element (default elem.name)
        :param type_func: Function to get the type of each element (default elem.type)
        :return: List with TemplateVarNode elements
    
    """
    names = []

    if not var_list:
        return names

    for elem in var_list:
        if func(elem):
            # Type string | var name | pointer to type | pointer to var | declaration string
            var = TemplateVarNode(var = elem, name_func = name_func, type_func = type_func, ast = ast, st = st)
            names.append(var)
    return names

# TODO: Clean (Move this to template class and generalize)
def get_typedefs_to_template(var_list,subtree):
    """ Build a list with all type declarations from a declaration list 
    
        :param var_list: List of declarations whose 
                            typedefs needs to be extracted
        :param subtree: Place to search the typedefs
    
    """
    
    decls_dict = {}
    
    for elem in var_list:
        try:
            # Search for  the type of the element
            identifier_type = IdentifierTypeFilter().apply(elem.type)
            # If the type has not been already added
            if not identifier_type.names[0] in decls_dict:
                # Look for its type definition in the subtree
                typedef_node = TypedefFilter(name = identifier_type.names[0])
                node = typedef_node.apply(subtree)
                # TODO: Avoid construction/destruction
                typedefIO = cStringIO.StringIO()
                cw = CWriter(stream = typedefIO)
                cw.visit(node)
                decls_dict[identifier_type.names[0]] = str(typedefIO.getvalue())
        except NodeNotFound as nnf:
#            print str(nnf)
            # It is not a complex type
            # print str(nnf)
            # print " Not a user defined-type " + str(elem)
            pass

    
    return [elem for elem in decls_dict.values()]
